// Copyright 2008 The Closure Library Authors. All Rights Reserved.
// Use of this source code is governed by the Apache License, Version 2.0.

goog.provide('goog.demos.editor.HelloWorldDialogPluginTest');
goog.setTestOnly('goog.demos.editor.HelloWorldDialogPluginTest');

goog.require('goog.demos.editor.HelloWorldDialog');
goog.require('goog.demos.editor.HelloWorldDialog.OkEvent');
goog.require('goog.demos.editor.HelloWorldDialogPlugin');
goog.require('goog.demos.editor.HelloWorldDialogPlugin.Command');
goog.require('goog.dom');
goog.require('goog.dom.NodeType');
goog.require('goog.editor.Field');
goog.require('goog.html.SafeHtml');
goog.require('goog.testing.ExpectedFailures');
goog.require('goog.testing.MockControl');
goog.require('goog.testing.MockRange');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.editor.FieldMock');
goog.require('goog.testing.editor.TestHelper');
goog.require('goog.testing.editor.dom');
goog.require('goog.testing.events');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.mockmatchers.ArgumentMatcher');
goog.require('goog.userAgent');

let plugin;
let mockCtrl;
let mockField;
let mockRange;
let mockPlaceCursorNextTo;
const stubs = new goog.testing.PropertyReplacer();

let fieldObj;

const CUSTOM_MESSAGE = 'Hello, cruel world...';

let expectedFailures;

function setUpPage() {
  expectedFailures = new goog.testing.ExpectedFailures();
}

function setUp() {
  mockCtrl = new goog.testing.MockControl();

  mockRange = new goog.testing.MockRange();
  mockCtrl.addMock(mockRange);

  mockField =
      new goog.testing.editor.FieldMock(undefined, undefined, mockRange);
  mockCtrl.addMock(mockField);

  mockPlaceCursorNextTo = mockCtrl.createFunctionMock('placeCursorNextTo');
}

function tearDown() {
  plugin.dispose();
  tearDownRealEditableField();
  expectedFailures.handleTearDown();
  stubs.reset();
  goog.dom.removeChildren(goog.dom.getElement('myField'));
}

/**
 * Tests that the plugin's dialog is properly created.
 */
function testCreateDialog() {
  mockField.$replay();

  plugin = new goog.demos.editor.HelloWorldDialogPlugin();
  plugin.registerFieldObject(mockField);

  const dialog = plugin.createDialog(goog.dom.getDomHelper());
  assertTrue('Dialog should be of type goog.demos.editor.HelloWorldDialog',
             dialog instanceof goog.demos.editor.HelloWorldDialog);

  mockField.$verify();
}

/**
 * Tests that when the OK event fires the editable field is properly updated.
 */
function testOk() {
  mockField.focus();
  mockField.dispatchBeforeChange();
  mockRange.removeContents();
  // Tests that an argument is a span with the custom message.
  const createdNodeMatcher =
      new goog.testing.mockmatchers.ArgumentMatcher(function(arg) {
        return arg.nodeType == goog.dom.NodeType.ELEMENT &&
               arg.tagName == goog.dom.TagName.SPAN &&
               goog.dom.getRawTextContent(arg) == CUSTOM_MESSAGE;
      });
  mockRange.insertNode(createdNodeMatcher, false);
  mockRange.$does(function(node, before) {
    return node;
  });
  mockPlaceCursorNextTo(createdNodeMatcher, false);
  stubs.set(goog.editor.range, 'placeCursorNextTo', mockPlaceCursorNextTo);
  mockField.dispatchSelectionChangeEvent();
  mockField.dispatchChange();
  mockCtrl.$replayAll();

  plugin = new goog.demos.editor.HelloWorldDialogPlugin();
  plugin.registerFieldObject(mockField);
  const dialog = plugin.createDialog(goog.dom.getDomHelper());

  // Mock of execCommand + clicking OK without actually opening the dialog.
  dialog.dispatchEvent(
      new goog.demos.editor.HelloWorldDialog.OkEvent(CUSTOM_MESSAGE));

  mockCtrl.$verifyAll();
}

/**
 * Setup a real editable field (instead of a mock) and register the plugin to
 * it.
 */
function setUpRealEditableField() {
  fieldObj = new goog.editor.Field('myField', document);
  fieldObj.makeEditable();
  // Register the plugin to that field.
  plugin = new goog.demos.editor.HelloWorldDialogPlugin();
  fieldObj.registerPlugin(plugin);
}

/**
 * Tear down the real editable field.
 */
function tearDownRealEditableField() {
  if (fieldObj) {
    fieldObj.makeUneditable();
    fieldObj.dispose();
  }
}

/**
 * Tests that the selection is cleared when the dialog opens and is
 * correctly restored after ok is clicked.
 */
function testRestoreSelectionOnOk() {
  setUpRealEditableField();

  fieldObj.setSafeHtml(false, goog.html.SafeHtml.htmlEscape('12345'));

  const elem = fieldObj.getElement();
  const helper = new goog.testing.editor.TestHelper(elem);
  helper.select('12345', 1, '12345', 4); // Selects '234'.

  assertEquals('Incorrect text selected before dialog is opened',
               '234',
               fieldObj.getRange().getText());
  plugin.execCommand(
      goog.demos.editor.HelloWorldDialogPlugin.Command.HELLO_WORLD_DIALOG);

  // TODO(user): IE returns some bogus range when field doesn't have
  // selection. Remove the expectedFailure when robbyw fixes the issue.
  // NOTE(user): You can't remove the selection from a field in Opera without
  // blurring it.
  elem.parentNode.blur();
  expectedFailures.expectFailureFor(goog.userAgent.IE ||
                                    goog.userAgent.OPERA);
  try {
    assertNull('There should be no selection while dialog is open',
               fieldObj.getRange());
  } catch (e) {
    expectedFailures.handleException(e);
  }

  goog.testing.events.fireClickSequence(
      plugin.dialog_.getOkButtonElement());
  assertEquals('No text should be selected after clicking ok',
               '',
               fieldObj.getRange().getText());

  // Test that the caret is placed after the custom message.
  goog.testing.editor.dom.assertRangeBetweenText(
      'Hello, world!', '5', fieldObj.getRange());
}
